package com.gt.quality.system.service.excel;


import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.write.metadata.WriteSheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.loadbalancer.CompletionContext;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @description: 处理Excel写入
 * @author: makeJava
 * @createTime: 2026-06-10 10:06
 * @since JDK21
 **/
public abstract class ExcelHandler {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());


    /**
     * Description: 默认一次5000条
     *
     * @author: makeJava
     */
    @Value("${excel.default.page.size:5000}")
    private int defaultPageSize;


    private ExecutorService defaultPools = null;

    /**
     * Description: simpleWrite在数据量不大的情况下可以使用 5000以内，具体也要看实际情况），
     * 创建excel对应的实体对象 参照{@link Object}
     *
     * @param fileName     文件名称
     * @param demoDataList 数据集合
     * @param sheetName    sheet页名称
     * @author: makeJava
     * @date: 2026-06-10 10:14:50
     */
    public void simpleWrite(String fileName, List<Object> demoDataList, String sheetName) {
        FastExcel.write(fileName, getEntityClass()).sheet(sheetName).doWrite(demoDataList);
    }


    /**
     * Description: 忽略 部分字段的写入
     *
     * @param fileName         文件名称
     * @param demoDataList     数据集合
     * @param sheetName        sheet页名称
     * @param ignoreFieldNames 忽略 部分字段
     * @author: makeJava
     * @date: 2026-06-10 10:17:59
     */
    public void simpleWriteAndIgnoreFiled(String fileName, List<Object> demoDataList, String sheetName, Set<String> ignoreFieldNames) {
        FastExcel.write(fileName, getEntityClass()).excludeColumnFieldNames(ignoreFieldNames).sheet(sheetName).doWrite(demoDataList);
    }

    /**
     * Description: 数据分批写到同一个sheet里面
     *
     * @param fileName  文件名称
     * @param totalSize 总条数
     * @param sheetName sheet页名称
     * @author: makeJava
     * @date: 2026-06-10 10:14:50
     */
    public void dataRepeatedWriteOneSheet(String fileName, int totalSize, String sheetName) {
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = FastExcel.write(fileName, getEntityClass()).build()) {
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = FastExcel.writerSheet(sheetName).build();
            // 去调用写入 实际使用时根据数据库分页的总的页数来
            int forRange = (totalSize % defaultPageSize) + 1;
            for (int currentIndex = 0; currentIndex < forRange; currentIndex++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<Object> data = callBackSelectPageData(currentIndex, defaultPageSize);
                excelWriter.write(data, writeSheet);
            }
        }
    }


    /**
     * Description: 数据分批写到不同sheet里面
     *
     * @param fileName        文件名称
     * @param totalSize       总条数
     * @param sheetNamePrefix sheet页前缀
     * @author: makeJava
     * @date: 2026-06-10 10:14:50
     */
    public void dataRepeatedWriteManySheet(String fileName, int totalSize, String sheetNamePrefix) {
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = FastExcel.write(fileName, getEntityClass()).build()) {
            // 去调用写入 实际使用时根据数据库分页的总的页数来
            int forRange = (totalSize % defaultPageSize) + 1;
            for (int currentIndex = 0; currentIndex < forRange; currentIndex++) {
                // 这里注意 如果同一个sheet只要创建一次
                WriteSheet writeSheet = FastExcel.writerSheet(sheetNamePrefix + currentIndex).build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<Object> data = callBackSelectPageData(currentIndex, defaultPageSize);
                excelWriter.write(data, writeSheet);
            }
        }
    }

    /**
     * Description: 数据分批写到不同sheet里面{异步}
     *
     * @param fileName        文件名称
     * @param totalSize       总条数
     * @param sheetNamePrefix sheet页前缀
     * @author: makeJava
     * @date: 2026-06-10 10:14:50
     */
    public boolean dataRepeatedWriteManySheetAsy(Class<?> entityClass, String fileName, int totalSize, String sheetNamePrefix) throws InterruptedException {
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = FastExcel.write(fileName, entityClass).build()) {

            // 去调用写入 实际使用时根据数据库分页的总的页数来
            int forRange = (totalSize % defaultPageSize) + 1;

            // 获取用户指定线程池--执行异步|||用户不指定就是用默认的线程池
            Executor threadPoolTaskExecutor = Optional.ofNullable(getThreadPoolTaskExecutor()).orElse(getThreadPoolTaskExecutorDefault(forRange));

            // 多线程--总线管理栏
            CountDownLatch countDownLatch = new CountDownLatch(forRange);

            // 多线程操作
            for (int currentIndex = 0; currentIndex < forRange; currentIndex++) {
                int finalIndex = currentIndex;
                threadPoolTaskExecutor.execute(() -> {
                    WriteSheet writeSheet = FastExcel.writerSheet(sheetNamePrefix + finalIndex).build();
                    // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                    List<Object> data = callBackSelectPageData(finalIndex, defaultPageSize);
                    excelWriter.write(data, writeSheet);
                    countDownLatch.countDown();
                });
            }

            // 等待全部完
            countDownLatch.await();
            logger.info(CompletionContext.Status.SUCCESS.name());
            if (defaultPools != null) {
                // 销毁临时线程池
                defaultPools.shutdown();
            }
            // 输出都其他位置
            return true;
        }
    }


    /**
     * Description: 目标分页查询回调
     *
     * @param currentIndex    当前页
     * @param defaultPageSize 每页查询条数
     * @author: makeJava
     * @date: 2026-06-10 10:31:07
     */
    public abstract List<Object> callBackSelectPageData(int currentIndex, int defaultPageSize);


    /**
     * Description: 目标实体对象
     *
     * @author: makeJava
     * @date: 2026-06-10 10:31:07
     */
    public abstract Class<?> getEntityClass();


    /**
     * Description: 调用系统自定义线程池
     *
     * @author: makeJava
     * @date: 2026-06-10 10:56:19
     */
    public abstract Executor getThreadPoolTaskExecutor();


    /**
     * Description: 默认创建定长线程
     *
     * @author: makeJava
     * @date: 2026-06-10 10:56:19
     */
    public Executor getThreadPoolTaskExecutorDefault(int coreThreadSize) {
        this.defaultPools = Executors.newFixedThreadPool(coreThreadSize);
        return defaultPools;
    }


}
